#pragma once

#include <iostream>
#include <sys/types.h> 
#include <sys/socket.h>
#include <string>
#include <cstring>
#include <strings.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "log.hpp"

//消息处理版本 （耦合度较高）
// using func_t = std::function<std::string(const std::string&)>;
typedef std::function<std::string(const std::string&, const std::string&, uint16_t)> func_t;

Log lg;
// extern Log lg; //extern声明（告诉编译器x在别的文件定义了）

enum {
    SOCEKT_ERR = 1,
    BIND_ERR
};

uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size = 1024;

class UdpServer {
public:
    UdpServer(const uint16_t& port = defaultport, const std::string& ip = defaultip) 
        :sockfd_(0), port_(port), ip_(ip), isrunning_(false) {}
    
    void Init() {
        //1. 创建udp socket
        //2. UDP的socket是全双工的，允许被同时读写
        //AF_INET - IPv4, SOCK_DGRAM - 表示使用数据报套接字，即UDP协议。
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0) {
            lg(Fatal, "socket create error, sockfd:%d", sockfd_);
            exit(SOCEKT_ERR);
        }
        lg(Info, "create socket success, sockfd:%d", sockfd_);

        //2.bind socket 绑定端口号
        //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 
        //const struct sockaddr *addr只是设计接口用，实际不使用
        struct sockaddr_in local;
        bzero(&local, sizeof(local));//清零
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);//要保证端口号是网络字节序列，因为要发给对方。htons主机序列转网络序列
        //sin_addr是结构体，sin_addr才是成员。inet_addr参数是const char* 
        local.sin_addr.s_addr = inet_addr(ip_.c_str());//1.string -> uint32_t  2.uint32_t必须是网络序列
        // local.sin_addr.s_addr = htonl(INADDR_ANY);//任意地址绑定。INADDR_ANY是全0，不存在网络字节序转换，htonl可以省略
        
        if (bind(sockfd_, (const struct sockaddr*)&local, sizeof(local)) < 0) {
            lg(Fatal, "bind error, errno:%d, err string:%s", errno, strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind success, errno:%d, err string:%s", errno, strerror(errno));
    }

    void CheckUser(const struct sockaddr_in& client, const std::string& clientip, const uint16_t& clientport) {
        auto iter = onlie_user_.find(clientip);
        if (iter == onlie_user_.end()) {
            onlie_user_.insert({clientip, client});
            std::cout << "[" << clientip << ":" << clientport << "]# add to online user list." << std::endl;
        }
    }

    void Broadcast(const std::string& info, const std::string& clientip, const uint16_t& clientport) {
        for (const auto& user : onlie_user_) {
            std::string message = "[";
            message += clientip;
            message += ":";
            message += clientport;
            message += "]# ";
            message += info;
            socklen_t len = sizeof(user.second);
            sendto(sockfd_, message.c_str(), message.size(), 0, (struct sockaddr*)&user.second, len);
            
        }
    }

    void Run() { //对代码进行分层
        isrunning_ = true;
        char inbuffer[size];
        cout << "server start running, waiting for message..." << endl;
        while (isrunning_) {
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            memset(inbuffer, 0, sizeof(inbuffer));
            ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer)-1, 0, (struct sockaddr*)&client, &len);
            if (n < 0) {
                lg(Warning, "recvfrom error, errno:%d, err string:%s", errno, strerror(errno));
                continue;
            }
            // inbuffer[n] = '\0';
            //检查收到该次信息的发送用户是否为新用户
            uint16_t clientport = ntohs(client.sin_port);//网络序列转主机序列
            std::string clientip = inet_ntoa(client.sin_addr);//整数IP转字符串IP
            CheckUser(client, clientip, clientport);

            //将消息广播给所有在线用户
            std::string info = inbuffer;
            Broadcast(info, clientip, clientport);
        }
    }

    //可以不关，析构对象时服务器也关了，进程也结束了。文件生命周期是随进程的
    ~UdpServer() { if (sockfd_ > 0) close(sockfd_); }
private:
    int sockfd_;//网络文件描述符
    std::string ip_;//任意地址bind 0
    uint16_t port_;//端口号
    bool isrunning_;
    std::unordered_map<std::string, struct sockaddr_in> onlie_user_;
    //维护在线用户列表  主机版本IP地址    网络版本套接字信息
};



// //聊天版本
// // using func_t = std::function<std::string(const std::string&)>;
// typedef std::function<std::string(const std::string&, const std::string&, uint16_t)> func_t;

// Log lg;
// // extern Log lg; //extern声明（告诉编译器x在别的文件定义了）

// enum {
//     SOCEKT_ERR = 1,
//     BIND_ERR
// };

// uint16_t defaultport = 8080;
// std::string defaultip = "0.0.0.0";
// const int size = 1024;

// class UdpServer {
// public:
//     UdpServer(const uint16_t& port = defaultport, const std::string& ip = defaultip) 
//         :sockfd_(0), port_(port), ip_(ip), isrunning_(false) {}
    
//     void Init() {
//         //1. 创建udp socket
//         //AF_INET - IPv4, SOCK_DGRAM - 表示使用数据报套接字，即UDP协议。
//         sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
//         if (sockfd_ < 0) {
//             lg(Fatal, "socket create error, sockfd:%d", sockfd_);
//             exit(SOCEKT_ERR);
//         }
//         lg(Info, "create socket success, sockfd:%d", sockfd_);

//         //2.bind socket 绑定端口号
//         //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 
//         //const struct sockaddr *addr只是设计接口用，实际不使用
//         struct sockaddr_in local;
//         bzero(&local, sizeof(local));//清零
//         local.sin_family = AF_INET;
//         local.sin_port = htons(port_);//要保证端口号是网络字节序列，因为要发给对方。htons主机序列转网络序列
//         //sin_addr是结构体，sin_addr才是成员。inet_addr参数是const char* 
//         local.sin_addr.s_addr = inet_addr(ip_.c_str());//1.string -> uint32_t  2.uint32_t必须是网络序列
//         // local.sin_addr.s_addr = htonl(INADDR_ANY);//任意地址绑定。INADDR_ANY是全0，不存在网络字节序转换，htonl可以省略
        
//         if (bind(sockfd_, (const struct sockaddr*)&local, sizeof(local)) < 0) {
//             lg(Fatal, "bind error, errno:%d, err string:%s", errno, strerror(errno));
//             exit(BIND_ERR);
//         }
//         lg(Info, "bind success, errno:%d, err string:%s", errno, strerror(errno));
//     }

//     void Run(func_t func) { //对代码进行分层
//         isrunning_ = true;
//         char inbuffer[size];
//         cout << "server start running, waiting for message..." << endl;
//         while (isrunning_) {
//             struct sockaddr_in client;
//             socklen_t len = sizeof(client);
//             ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer)-1, 0, (struct sockaddr*)&client, &len);
//             if (n < 0) {
//                 lg(Warning, "recvfrom error, errno:%d, err string:%s", errno, strerror(errno));
//                 continue;
//             }
//             uint16_t clientport = ntohs(client.sin_port);//网络序列转主机序列
//             std::string clientip = inet_ntoa(client.sin_addr);//整数IP转字符串IP

//             //充当一次数据处理
//             inbuffer[n] = 0;
//             std::string info = inbuffer;
//             std::string echo_string = func(info, clientip, clientport);
//             sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&client, len);
//         }
//     }

//     //可以不关，析构对象时服务器也关了，进程也结束了。文件生命周期是随进程的
//     ~UdpServer() { if (sockfd_ > 0) close(sockfd_); }
// private:
//     int sockfd_;//网络文件描述符
//     std::string ip_;//任意地址bind 0
//     uint16_t port_;//端口号
//     bool isrunning_;
// };




// //接收数据和处理数据解耦版本
// // using func_t = std::function<std::string(const std::string&)>;
// typedef std::function<std::string(const std::string&)> func_t;

// Log lg;
// // extern Log lg; //extern声明（告诉编译器x在别的文件定义了）

// enum {
//     SOCEKT_ERR = 1,
//     BIND_ERR
// };

// uint16_t defaultport = 8080;
// std::string defaultip = "0.0.0.0";
// const int size = 1024;

// class UdpServer {
// public:
//     UdpServer(const uint16_t& port = defaultport, const std::string& ip = defaultip) 
//         :sockfd_(0), port_(port), ip_(ip), isrunning_(false) {}
    
//     void Init() {
//         //1. 创建udp socket
//         //AF_INET - IPv4, SOCK_DGRAM - 表示使用数据报套接字，即UDP协议。
//         sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
//         if (sockfd_ < 0) {
//             lg(Fatal, "socket create error, sockfd:%d", sockfd_);
//             exit(SOCEKT_ERR);
//         }
//         lg(Info, "create socket success, sockfd:%d", sockfd_);

//         //2.bind socket 绑定端口号
//         //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 
//         //const struct sockaddr *addr只是设计接口用，实际不使用
//         struct sockaddr_in local;
//         bzero(&local, sizeof(local));//清零
//         local.sin_family = AF_INET;
//         local.sin_port = htons(port_);//要保证端口号是网络字节序列，因为要发给对方。htons主机序列转网络序列
//         //sin_addr是结构体，sin_addr才是成员。inet_addr参数是const char* 
//         local.sin_addr.s_addr = inet_addr(ip_.c_str());//1.string -> uint32_t  2.uint32_t必须是网络序列
//         // local.sin_addr.s_addr = htonl(INADDR_ANY);//任意地址绑定。INADDR_ANY是全0，不存在网络字节序转换，htonl可以省略
        
//         if (bind(sockfd_, (const struct sockaddr*)&local, sizeof(local)) < 0) {
//             lg(Fatal, "bind error, errno:%d, err string:%s", errno, strerror(errno));
//             exit(BIND_ERR);
//         }
//         lg(Info, "bind success, errno:%d, err string:%s", errno, strerror(errno));
//     }

//     void Run(func_t func) { //对代码进行分层
//         isrunning_ = true;
//         char inbuffer[size];
//         cout << "server start running, waiting for message..." << endl;
//         while (isrunning_) {
//             struct sockaddr_in client;
//             socklen_t len = sizeof(client);
//             ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer)-1, 0, (struct sockaddr*)&client, &len);
//             if (n < 0) {
//                 lg(Warning, "recvfrom error, errno:%d, err string:%s", errno, strerror(errno));
//                 continue;
//             }
//             //充当一次数据处理
//             inbuffer[n] = 0;
//             std::string info = inbuffer;
//             std::string echo_string = func(info);
//             sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&client, len);
//         }
//     }

//     //可以不关，析构对象时服务器也关了，进程也结束了。文件生命周期是随进程的
//     ~UdpServer() { if (sockfd_ > 0) close(sockfd_); }
// private:
//     int sockfd_;//网络文件描述符
//     std::string ip_;//任意地址bind 0
//     uint16_t port_;//端口号
//     bool isrunning_;
// };



// // Log lg;
// extern Log lg; //extern声明（告诉编译器x在别的文件定义了）

// enum {
//     SOCEKT_ERR = 1,
//     BIND_ERR
// };

// uint16_t defaultport = 8080;
// std::string defaultip = "0.0.0.0";
// const int size = 1024;

// class UdpServer {
// public:
//     UdpServer(const uint16_t& port = defaultport, const std::string& ip = defaultip) 
//         :sockfd_(0), port_(port), ip_(ip), isrunning_(false) {}
    
//     void Init() {
//         //1. 创建udp socket
//         //AF_INET - IPv4, SOCK_DGRAM - 表示使用数据报套接字，即UDP协议。
//         sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
//         if (sockfd_ < 0) {
//             lg(Fatal, "socket create error, sockfd:%d", sockfd_);
//             exit(SOCEKT_ERR);
//         }
//         lg(Info, "create socket success, sockfd:%d", sockfd_);

//         //2.bind socket 绑定端口号
//         //int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 
//         //const struct sockaddr *addr只是设计接口用，实际不使用
//         struct sockaddr_in local;
//         bzero(&local, sizeof(local));//清零
//         local.sin_family = AF_INET;
//         local.sin_port = htons(port_);//要保证端口号是网络字节序列，因为要发给对方。htons主机序列转网络序列
//         //sin_addr是结构体，sin_addr才是成员。inet_addr参数是const char* 
//         local.sin_addr.s_addr = inet_addr(ip_.c_str());//1.string -> uint32_t  2.uint32_t必须是网络序列
//         // local.sin_addr.s_addr = htonl(INADDR_ANY);//任意地址绑定。INADDR_ANY是全0，不存在网络字节序转换，htonl可以省略
        
//         if (bind(sockfd_, (const struct sockaddr*)&local, sizeof(local)) < 0) {
//             lg(Fatal, "bind error, errno:%d, err string:%s", errno, strerror(errno));
//             exit(BIND_ERR);
//         }
//         lg(Info, "bind success, errno:%d, err string:%s", errno, strerror(errno));
//     }

//     void Run() {
//         isrunning_ = true;
//         char inbuffer[size];
//         cout << "server start running, waiting for message..." << endl;
//         while (isrunning_) {
//             struct sockaddr_in client;
//             socklen_t len = sizeof(client);
//             ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer)-1, 0, (struct sockaddr*)&client, &len);
//             if (n < 0) {
//                 lg(Warning, "recvfrom error, errno:%d, err string:%s", errno, strerror(errno));
//                 continue;
//             }
//             //充当一次数据处理
//             inbuffer[n] = 0;
//             std::string info = inbuffer;
//             std::string echo_string = "server echo# " + info;
//             cout << echo_string << endl;

//             sendto(sockfd_, echo_string.c_str(), echo_string.size(), 0, (struct sockaddr*)&client, len);
//         }
//     }

//     ~UdpServer() { if (sockfd_ > 0) close(sockfd_); }
// private:
//     int sockfd_;//网络文件描述符
//     std::string ip_;//任意地址bind 0
//     uint16_t port_;//端口号
//     bool isrunning_;
// };


